Wp 5257/create durable nonce solana recovery#70
Conversation
7ee0805 to
91f0ce7
Compare
| let unsignedSweepPrebuildTx: Awaited<ReturnType<typeof recoverEddsaWallets>>; | ||
| if (sdkCoin.getFamily() === CoinFamily.SOL) { | ||
| const solanaParams = params.coinSpecificParams as SolanaRecoveryOptions; | ||
| console.log(params); |
| let unsignedSweepPrebuildTx: Awaited<ReturnType<typeof recoverEddsaWallets>>; | ||
| if (sdkCoin.getFamily() === CoinFamily.SOL) { | ||
| const solanaParams = params.coinSpecificParams as SolanaRecoveryOptions; | ||
| console.log(params); |
| walletContractAddress: '', | ||
| unsignedSweepPrebuildTx: undefined, | ||
| coinSpecificParams: undefined, | ||
| coinSpecificParams: coinSpecificParams?.solanaRecoveryOptions, |
There was a problem hiding this comment.
kinda oddd to have solanaRecoveryOptions for handleEddsaRecovery.
There was a problem hiding this comment.
yeah but for now it's the only coin with additional options, so kept it like this.
| // Solana specific recovery parameters | ||
| solanaRecoveryOptions: t.partial({ |
There was a problem hiding this comment.
are these solana specific, or eddsa specific? or is a subset of these for solana only?
There was a problem hiding this comment.
solana specific.
There was a problem hiding this comment.
woudnt this be confusing then? what would be the expected params for non-sol eddsa recoveries?
There was a problem hiding this comment.
export interface MPCRecoveryOptions {
userKey?: string; // Box A
backupKey?: string; // Box B
bitgoKey: string; // Box C - this is bitgo's xpub and will be used to derive their root address
recoveryDestination: string;
walletPassphrase?: string;
seed?: string;
index?: number;
// the starting receive address index to scan from for WRW recovery
startingScanIndex?: number;
// the number of addresses to scan from the starting index
scan?: number;
// token contract address of the token to recover
tokenContractAddress?: string;
apiKey?: string;
}```
that's the recovery options for other eddsa coins, so those options are already encapsulated in our base parameters. Only sol has additional options.
| }; | ||
| let unsignedSweepPrebuildTx: Awaited<ReturnType<typeof recoverEddsaWallets>>; | ||
| if (sdkCoin.getFamily() === CoinFamily.SOL) { | ||
| const solanaParams = params.coinSpecificParams as SolanaRecoveryOptions; |
There was a problem hiding this comment.
why are we not using params.solanaSpecificParams that u added here?
There was a problem hiding this comment.
poor structuring of the parameters
it's passed in as
coinSpecificParams: coinSpecificParams?.solanaRecoveryOptions,
Ticket: WP-5300 # Conflicts: # src/api/master/routers/masterApiSpec.ts
91f0ce7 to
50a2edb
Compare
There was a problem hiding this comment.
Pull Request Overview
This PR implements durable nonce support for Solana recovery and restructures the coin-specific parameters for recovery operations. The changes include creating reusable transaction utilities, standardizing error handling, and expanding parameter validation for different coin types.
Key changes:
- Adds transaction utility functions for handling various transaction formats
- Implements structured coin-specific recovery parameters for UTXO, EVM, and Solana coins
- Introduces comprehensive error handling classes with appropriate HTTP status codes
- Adds support for Solana durable nonce in recovery operations
Reviewed Changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| src/shared/transactionUtils.ts | New utility module for transaction type checking and data extraction |
| src/shared/errors.ts | New custom error classes with HTTP status code mapping |
| src/shared/responseHandler.ts | Updates error handling to support new custom error types |
| src/api/master/routers/masterApiSpec.ts | Restructures API spec with coin-specific recovery parameters |
| src/api/master/handlers/recoveryWallet.ts | Implements parameter validation and Solana durable nonce support |
| src/api/master/clients/enclavedExpressClient.ts | Refactors to use new transaction utility functions |
| src/tests/api/master/recoveryWallet.test.ts | Comprehensive test coverage for parameter validation |
| masterBitgoExpress.json | Updates JSON schema to reflect new parameter structure |
| }); | ||
| }; | ||
| let unsignedSweepPrebuildTx: Awaited<ReturnType<typeof recoverEddsaWallets>>; | ||
| if (sdkCoin.getFamily() === CoinFamily.SOL) { |
There was a problem hiding this comment.
Type assertion without null checking could cause runtime errors. The coinSpecificParams could be undefined, and accessing properties on solanaParams would fail if it's undefined.
| if (sdkCoin.getFamily() === CoinFamily.SOL) { | |
| if (sdkCoin.getFamily() === CoinFamily.SOL) { | |
| if (!params.coinSpecificParams) { | |
| throw new ValidationError('coinSpecificParams is required for Solana recovery'); | |
| } |
| txRequest.signableHex = firstTransaction.unsignedTx?.serializedTx || ''; | ||
| txRequest.derivationPath = firstTransaction.unsignedTx?.derivationPath || ''; | ||
| } else { | ||
| throw new Error(`Unrecognized transaction ${JSON.stringify(tx)}`); |
There was a problem hiding this comment.
JSON.stringify on untrusted input could potentially expose sensitive data or cause performance issues with large objects. Consider logging a generic error message instead.
| throw new Error(`Unrecognized transaction ${JSON.stringify(tx)}`); | |
| throw new Error('Unrecognized transaction format.'); |
There was a problem hiding this comment.
Error would be thrown back to client, not a concern.
| * Type guard to check if the object is an MPCTxs format | ||
| * MPCTxs format has a txRequests array containing transaction information | ||
| */ | ||
| export function isMPCTxs(tx: any): tx is MPCTxs { |
There was a problem hiding this comment.
[nitpick] Using 'any' type reduces type safety. Consider using 'unknown' instead to force proper type checking at call sites.
| export function isMPCTxs(tx: any): tx is MPCTxs { | |
| export function isMPCTxs(tx: unknown): tx is MPCTxs { |
| } | ||
|
|
||
| export function isMPCUnsignedTx(tx: any): tx is MPCUnsignedTx { | ||
| return 'unsignedTx' in tx && isMPCTx(tx); |
There was a problem hiding this comment.
The type guard logic is incorrect. isMPCUnsignedTx should check for 'unsignedTx' property but then calls isMPCTx which checks for 'signableHex'. An MPCUnsignedTx likely has a different structure than MPCTx.
| return 'unsignedTx' in tx && isMPCTx(tx); | |
| return tx && 'unsignedTx' in tx && typeof tx.unsignedTx === 'object'; |
There was a problem hiding this comment.
| const solanaRecoveryOptions: SolRecoveryOptions = { ...options }; | ||
| solanaRecoveryOptions.recoveryDestinationAtaAddress = | ||
| solanaParams.recoveryDestinationAtaAddress; | ||
| solanaRecoveryOptions.closeAtaAddress = solanaParams.closeAtaAddress; | ||
| solanaRecoveryOptions.tokenContractAddress = solanaParams.tokenContractAddress; | ||
| solanaRecoveryOptions.programId = solanaParams.programId; | ||
| if (solanaParams.durableNonce) { | ||
| solanaRecoveryOptions.durableNonce = { | ||
| publicKey: solanaParams.durableNonce.publicKey, | ||
| secretKey: solanaParams.durableNonce.secretKey, | ||
| }; | ||
| } |
There was a problem hiding this comment.
[nitpick] The spread operator creates a shallow copy, but then individual properties are assigned. Consider directly constructing the object to be more explicit about the intended structure.
| const solanaRecoveryOptions: SolRecoveryOptions = { ...options }; | |
| solanaRecoveryOptions.recoveryDestinationAtaAddress = | |
| solanaParams.recoveryDestinationAtaAddress; | |
| solanaRecoveryOptions.closeAtaAddress = solanaParams.closeAtaAddress; | |
| solanaRecoveryOptions.tokenContractAddress = solanaParams.tokenContractAddress; | |
| solanaRecoveryOptions.programId = solanaParams.programId; | |
| if (solanaParams.durableNonce) { | |
| solanaRecoveryOptions.durableNonce = { | |
| publicKey: solanaParams.durableNonce.publicKey, | |
| secretKey: solanaParams.durableNonce.secretKey, | |
| }; | |
| } | |
| const solanaRecoveryOptions: SolRecoveryOptions = { | |
| bitgoKey: options.bitgoKey, | |
| recoveryDestination: options.recoveryDestination, | |
| apiKey: options.apiKey, | |
| recoveryDestinationAtaAddress: solanaParams.recoveryDestinationAtaAddress, | |
| closeAtaAddress: solanaParams.closeAtaAddress, | |
| tokenContractAddress: solanaParams.tokenContractAddress, | |
| programId: solanaParams.programId, | |
| durableNonce: solanaParams.durableNonce | |
| ? { | |
| publicKey: solanaParams.durableNonce.publicKey, | |
| secretKey: solanaParams.durableNonce.secretKey, | |
| } | |
| : undefined, | |
| }; |
No description provided.